﻿// -----------------------------------------------------------------------
// <copyright file="RC4.cs" company="Hewlett-Packard">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------

namespace SecutityLib
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Linq;
    using System.Text;

    /// <summary>
    /// TODO: Update summary.
    /// </summary>
    public class RC4 : EncodingDecodingTech
    {
        /// <summary>
        /// s vector
        /// </summary>
        private byte[] s;

        /// <summary>
        /// t vector
        /// </summary>
        private byte[] t;

        /// <summary>
        /// Encodes the specified plain text.
        /// </summary>
        /// <param name="plainText">The plain text.</param>
        /// <param name="key">The key.</param>
        /// <returns>
        /// cipher text
        /// </returns>
        public override string Encode(string plainText, string key)
        {
            string cipher = string.Empty;

            byte[] keyBytes = this.GetBytesOfString(key);
            byte[] text = this.GetBytesOfString(plainText);

            this.InitializeS(keyBytes);
            this.Initialpermutation();

            int i = 0, j = 0;

            int len = text.Length;
            string cipher2 = string.Empty;
            for (int l = 0; l < len; l++)
            {
                i = (i + 1) % 256;
                j = (j + this.s[i]) % 256;
                this.Swap(i, j);
                int temp = (this.s[i] + this.s[j]) % 256;
                byte k = this.s[temp];

                byte[] tempbyteArr = new byte[1];
                tempbyteArr[0] = (byte)(k ^ text[l]);
                cipher2 += (char)tempbyteArr[0];
                cipher += BitConverter.ToString(tempbyteArr, 0);
                cipher += " ";
            }

            return cipher + "\r\n" + cipher2;
        }

        /// <summary>
        /// Decodes the specified plain text.
        /// </summary>
        /// <param name="cipherText">The cipher text.</param>
        /// <param name="key">The key.</param>
        /// <returns>
        /// cipher text
        /// </returns>
        public override string Decode(string cipherText, string key)
        {
            return this.Encode(cipherText, key);
        }

        /// <summary>
        /// Initialpermutations this instance.
        /// </summary>
        private void Initialpermutation()
        {
            int j = 0;
            for (int i = 0; i < 256; i++)
            {
                j = (j + this.s[i] + this.t[i]) % 256;
                this.Swap(i, j);
            }
        }

        /// <summary>
        /// Initializes the S.
        /// </summary>
        /// <param name="key">The key.</param>
        private void InitializeS(byte[] key)
        {
            this.s = new byte[256];
            this.t = new byte[256];

            int keyLen = key.Length;

            for (int i = 0; i < 256; i++)
            {
                this.s[i] = (byte)i;
                this.t[i] = key[i % keyLen];
            }
        }

        /// <summary>
        /// Gets the bytes of key.
        /// </summary>
        /// <param name="str">The key.</param>
        /// <returns>key as bytes</returns>
        private byte[] GetBytesOfString(string str)
        {
            if (str[0] == '0' && str[1] == 'x')
            {
                str = str.Substring(2);
                if (str.Length % 2 != 0)
                {
                    throw new Exception("text must be in the bytes format");
                }

                byte[] keyBytes = new byte[str.Length / 2];
                for (int i = 0; i < str.Length; i += 2)
                {
                    keyBytes[i / 2] = byte.Parse(str.Substring(i, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
                }

                return keyBytes;
            }
            else
            {
                byte[] keyBytes = new byte[str.Length];
                for (int i = 0; i < str.Length; i++)
                {
                    keyBytes[i] = (byte)str[i];
                }

                return keyBytes;
            }
        }

        /// <summary>
        /// Swaps the specified index1.
        /// </summary>
        /// <param name="index1">The index1.</param>
        /// <param name="index2">The index2.</param>
        private void Swap(int index1, int index2)
        {
            byte temp = this.s[index1];
            this.s[index1] = this.s[index2];
            this.s[index2] = temp;
        }
    }
}
